package com.unknown.chainevent.biz.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unknown.chainevent.biz.service.ChainConfigService;
import com.unknown.chainevent.biz.service.ContractLogsService;
import com.unknown.chainevent.common.constants.ChainEventConstants;
import com.unknown.chainevent.common.entity.ChainConfig;
import com.unknown.chainevent.common.entity.ContractLogs;
import com.unknown.chainevent.common.mapper.ContractLogsMapper;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.http.HttpService;

@Slf4j
@Service
public class ContractLogsServiceImpl extends ServiceImpl<ContractLogsMapper, ContractLogs> implements ContractLogsService {

    @Autowired
    private ChainConfigService chainConfigService;

    @Override
    public int insertSelective(ContractLogs record) {
        return baseMapper.insertSelective(record);
    }

    @Override
    public int updateByPrimaryKeySelective(ContractLogs record) {
        return baseMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public boolean existsByHashAndIndex(String hash, Long index) {
        return getBaseMapper().countByHashAndIndex(hash, index) > 0;
    }

    @Override
    public void processTransactionConfirm() {
        List<ContractLogs> logsList = list(
            Wrappers.lambdaQuery(ContractLogs.class)
                .eq(ContractLogs::getConfirmStatus, ChainEventConstants.ContractLogConfirmStatus.AWAIT.getStatus())
        );
        if (ObjectUtils.isEmpty(logsList)) {
            return;
        }
        //按链分组
        Map<Long, List<ContractLogs>> chainToLogs = logsList.stream().collect(Collectors.groupingBy(ContractLogs::getChainConfigId));
        for (List<ContractLogs> _logList : chainToLogs.values()) {
            Long chainConfigId = _logList.get(0).getChainConfigId();
            ChainConfig chainConfig = chainConfigService.getById(chainConfigId);
            //这条链需要的确认区块数
            Integer confirmBlock = chainConfig.getConfirmBlock();
            Web3j web3j = Web3j.build(new HttpService(chainConfig.getRpcAddress()));
            try {
                //当前区块高度
                long currBLockNumber = web3j.ethBlockNumber().send().getBlockNumber().longValue();
                //要处理的区块高度（高于这个高度，确认数没达到不处理）
                long processBlock = currBLockNumber - confirmBlock;
                //取出所有交易hash（用set去重）
                Set<String> hashSet = _logList.stream()
                    .filter(_v -> _v.getBlockNumber() <= processBlock)
                    .map(ContractLogs::getTransHash)
                    .collect(Collectors.toSet());
                for (String hash : hashSet) {
                    try {
                        EthGetTransactionReceipt ethGetTransactionReceipt = web3j.ethGetTransactionReceipt(hash).send();
                        if (ethGetTransactionReceipt.hasError()) {
                            //请求错误，暂不处理
                            log.warn("确认Log交易收据-获取属于请求异常-[hash:{}, errorMsg:{}]",
                                hash, ethGetTransactionReceipt.getError() != null ? ethGetTransactionReceipt.getError().getMessage() : "");
                            continue;
                        }
                        ChainEventConstants.ContractLogConfirmStatus confirmStatus = null;
                        if (ethGetTransactionReceipt.getTransactionReceipt().isEmpty()) {
                            confirmStatus = ChainEventConstants.ContractLogConfirmStatus.FAIL;
                            log.warn("确认Log交易收据-收据为空-[hash:{}]", hash);
                        } else if (!ethGetTransactionReceipt.getTransactionReceipt().get().isStatusOK()) {
                            confirmStatus = ChainEventConstants.ContractLogConfirmStatus.FAIL;
                            log.warn("确认Log交易收据-收据状态为无效-[hash:{}]", hash);
                        } else if (ethGetTransactionReceipt.getTransactionReceipt().get().isStatusOK()) {
                            confirmStatus = ChainEventConstants.ContractLogConfirmStatus.SUCCESS;
                        }
                        //更新状态
                        getBaseMapper().updateConfirmStatus(hash, confirmStatus.getStatus(),
                            ChainEventConstants.ContractLogConfirmStatus.AWAIT.getStatus());

                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatusSuccess(Long id) {
        int i = getBaseMapper().updateStatusSuccess(id);
        Assert.isTrue(i > 0, "更新log状态为success失败");
    }

    @Override
    public List<ContractLogs> selectLogsByContractName(String firstTopic, String contractAddress, String contractName, String targetTable,Integer limit) {
        return baseMapper.selectLogsByContractName(firstTopic, contractAddress, contractName, targetTable,limit);
    }
    @Override
    public List<ContractLogs> selectLogsByContractName(String firstTopic, String contractName, String targetTable,Integer limit) {
        return baseMapper.selectLogsByContractName(firstTopic, null, contractName, targetTable,limit);
    }
    @Override
    public List<ContractLogs> selectLogsByContractName(String firstTopic, String contractName, String targetTable) {
        return baseMapper.selectLogsByContractName(firstTopic, null, contractName, targetTable,100);
    }
}
